package com.foreverwin.mes.so.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.foreverwin.mes.common.constants.HandleBOConstants;
import com.foreverwin.mes.core.base.FrontPage;
import com.foreverwin.mes.core.exception.BusinessException;
import com.foreverwin.mes.core.utils.StringUtils;
import com.foreverwin.mes.meapi.enums.CustomFieldsConstant;
import com.foreverwin.mes.meapi.model.CustomFields;
import com.foreverwin.mes.meapi.service.CustomFieldsService;
import com.foreverwin.mes.so.mapper.RecipeMapper;
import com.foreverwin.mes.so.model.*;
import com.foreverwin.mes.so.service.RecipeParamService;
import com.foreverwin.mes.so.service.RecipeResrceItemRelService;
import com.foreverwin.mes.so.service.RecipeService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.SQLIntegrityConstraintViolationException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author Syngna
 * @since 2020-02-04
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class RecipeServiceImpl extends ServiceImpl<RecipeMapper, Recipe> implements RecipeService {

    private final Logger logger = LoggerFactory.getLogger(RecipeServiceImpl.class);

    @Autowired
    private RecipeMapper recipeMapper;

    @Autowired
    private RecipeParamService recipeParamService;

    @Autowired
    private RecipeResrceItemRelService recipeResrceItemRelService;

    @Autowired
    private CustomFieldsService customFieldsService;

    @Override
    public Page<Recipe> selectPage(FrontPage<Recipe> frontPage, Recipe recipe) {
        EntityWrapper<Recipe> queryWrapper = new EntityWrapper<>();
        queryWrapper.setEntity(recipe);
        return super.selectPage(frontPage.getPagePlus(), queryWrapper);
    }

    @Override
    public List<Recipe> selectList(Recipe recipe) {
        EntityWrapper<Recipe> queryWrapper = new EntityWrapper<>();
        queryWrapper.setEntity(recipe);
        return super.selectList(queryWrapper);
    }

    /**
     * 获取配方参数
     * @param site
     * @param recipe
     * @param revision
     * @return
     */
    @Override
    public List<RecipeParam> listParams(String site, String recipe, String revision) {
        Recipe recipePo = this.selectOne(new EntityWrapper<Recipe>()
                .eq(Recipe.SITE, site)
                .eq(Recipe.RECIPE, recipe)
                .eq(Recipe.REVISION, revision));
        if(recipePo == null) {
            return new ArrayList<>();
        }
        return recipeParamService.selectList(new EntityWrapper<RecipeParam>().eq(RecipeParam.RECIPE_BO, recipePo.getHandle()));
    }

    /**
     * 创建新配方
     * @param site
     * @param user
     * @param newRecipe
     * @param newRecipeVer
     * @param originRecipe 复制数据自originRecipe配方
     * @param originRecipeVer 复制数据自originRecipeVer版本
     */
    @Override
    public void add(String site, String user, String newRecipe, String newRecipeVer, String originRecipe, String originRecipeVer) {
        Recipe newRecipePo = new Recipe();
        Date now = new Date();
        String newRecipeBo = StringUtils.genHandle(HandleBOConstants.RECIPE_BO, site, newRecipe, newRecipeVer);
        newRecipePo.setHandle(newRecipeBo);
        newRecipePo.setSite(site);
        newRecipePo.setRecipe(newRecipe);
        newRecipePo.setRevision(newRecipeVer);
        newRecipePo.setUsed("N");
        newRecipePo.setCreator(user);
        newRecipePo.setUpdater(user);
        newRecipePo.setCreatedDateTime(now);
        newRecipePo.setModifiedDateTime(now);

        if(StringUtils.notBlank(originRecipe)) {
            if(StringUtils.isBlank(originRecipeVer)) {
                throw BusinessException.build("复制原始配方版本不能为空");
            }
            List<Recipe> originRecipeList = this.selectList(new EntityWrapper<Recipe>()
                    .eq(Recipe.SITE, site)
                    .eq(Recipe.RECIPE, originRecipe)
                    .eq(Recipe.REVISION, originRecipeVer));
            if(originRecipeList == null || originRecipeList.size() == 0) {
                throw BusinessException.build("原始配方【" + originRecipe + "】版本【"+ originRecipeVer +"】不存在");
            }
            // 复制原始配方参数到新配方
            List<RecipeParam> recipeParamList = recipeParamService.selectList(new EntityWrapper<RecipeParam>()
                    .eq(RecipeParam.RECIPE_BO, originRecipeList.get(0).getHandle()));
            for(RecipeParam recipeParam: recipeParamList) {
                recipeParam.setHandle(StringUtils.genHandle(HandleBOConstants.RECIPE_PARAM_BO, site, newRecipeBo, recipeParam.getParamName()));
                recipeParam.setRecipeBo(newRecipeBo);
                recipeParam.setUpdater(user);
                recipeParam.setCreatedDateTime(now);
                recipeParam.setModifiedDateTime(now);
            }
            if(recipeParamList != null && recipeParamList.size() > 0) {
                recipeParamService.insertBatch(recipeParamList);
            }
        }
        this.insert(newRecipePo);
    }

    /**
     * 删除参数
     * @param recipeView
     */
    @Override
    public void deleteParams(String user, RecipeView recipeView) {
        String recipeNo = recipeView.getRecipe();
        String recipeVersion = recipeView.getRevision();
        List<String> paramIds = recipeView.getParamHandleList();
        if(paramIds == null || paramIds.size() == 0) {
            throw BusinessException.build("没有要删除的数据");
        }
        RecipeParam recipeParam = recipeParamService.selectById(paramIds.get(0));
        Recipe recipe = this.selectById(recipeParam.getRecipeBo());
        if(!recipe.getRecipe().equals(recipeNo) || !recipe.getRevision().equals(recipeVersion)) {
            throw BusinessException.build("配方数据已更改，请重新检索");
        }
        if("Y".equals(recipe.getUsed())) {
            throw BusinessException.build("配方数据已被使用不允许删除！");
        }
        recipeParamService.deleteBatchIds(paramIds);
        logger.info("RECIPE_PARAM_OPERATION: 用户【{}】删除配方参数【{}】成功!", user, paramIds);
    }

    @Override
    public void addParam(String site, String user, RecipeParam recipeParam) {
        String recipeName = recipeParam.getRecipe();
        String recipeVer = recipeParam.getRecipeRevision();
        List<Recipe> recipeList = this.selectList(new EntityWrapper<Recipe>()
                .eq(Recipe.SITE, site)
                .eq(Recipe.RECIPE, recipeName)
                .eq(Recipe.REVISION, recipeVer));
        if(recipeList == null || recipeList.size() == 0) {
            throw BusinessException.build("配方【" + recipeName + "】版本【"+ recipeVer +"】不存在, 请先创建配方");
        }
        Date now = new Date();
        String recipeBo = recipeList.get(0).getHandle();
        recipeParam.setRecipeBo(recipeBo);
        recipeParam.setHandle(StringUtils.genHandle(HandleBOConstants.RECIPE_PARAM_BO, site, recipeParam.getRecipeBo(), recipeParam.getParamName()));
        recipeParam.setCreator(user);
        recipeParam.setUpdater(user);
        recipeParam.setCreatedDateTime(now);
        recipeParam.setModifiedDateTime(now);
        try{
            recipeParamService.insert(recipeParam);
        } catch (Exception e) {
            if( e instanceof SQLIntegrityConstraintViolationException || e instanceof DuplicateKeyException) {
                throw new MybatisPlusException("重复参数 '" + recipeParam.getParamName() + "'");
            } else {
                throw e;
            }
        }
        logger.info("RECIPE_PARAM_OPERATION: 用户【{}】新增配方参数{}成功!", user, JSON.toJSONString(recipeParam, SerializerFeature.WriteDateUseDateFormat));
    }

    @Override
    public void updateParam(String site, String user, RecipeParam recipeParam) {
        Date now = new Date();
        recipeParam.setUpdater(user);
        recipeParam.setModifiedDateTime(now);
        recipeParamService.updateAllColumnById(recipeParam);
        logger.info("RECIPE_PARAM_OPERATION: 用户【{}】更新配方参数{}成功!", user, JSON.toJSONString(recipeParam, SerializerFeature.WriteDateUseDateFormat));
    }

    /**
     * 根据物料和设备获取配方信息
     * @param site
     * @param item
     * @param resource
     * @return
     */
    @Override
    public Recipe getRecipeInfo(String site, String item, String resource) {
        // 设备维护是否需要配方信息，不需要则不用校验配方信息不存在
        CustomFields reciveRecipeCustomFields = customFieldsService.getAttributeValue(StringUtils.genHandle(HandleBOConstants.RESOURCE_BO, site, resource), CustomFieldsConstant.KW_RESOURCE_RECEIVE_RECIPE);
        String reciveRecipe = reciveRecipeCustomFields == null ? "N" : reciveRecipeCustomFields.getValue();

        List<RecipeResrceItemRelView> recipeResrceItemRelViewList = recipeResrceItemRelService.findRelation(site, resource, item);
        if(recipeResrceItemRelViewList == null || recipeResrceItemRelViewList.size() == 0) {
            if(!"Y".equalsIgnoreCase(reciveRecipe)) {
                return null;
            }
            throw BusinessException.build("设备【" + resource + "】物料【" + item + "】未找到配方信息");
        } else if(recipeResrceItemRelViewList.size() > 1) {
            throw BusinessException.build("设备【" + resource + "】物料【" + item + "】找到多条配方信息，请确认设定资料是否正确");
        }
        RecipeResrceItemRelView recipeResrceItemRelView = recipeResrceItemRelViewList.get(0);
        String recipeHandle = recipeResrceItemRelView.getRecipeBo();
        Recipe recipe = this.selectById(recipeHandle);
        if(recipe == null) {
            if(!"Y".equalsIgnoreCase(reciveRecipe)) {
                return null;
            }
            throw BusinessException.build("设备【" + resource + "】物料【" + item + "】对应的配方【"  + recipeHandle +"】不存在");
        }
        List<RecipeParam> recipeParamList = recipeParamService.selectList(new EntityWrapper<RecipeParam>().eq(RecipeParam.RECIPE_BO, recipeHandle));
        recipe.setRecipeParamList(recipeParamList);
        return recipe;
    }
}